package com.wdb.modules.security.security;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.wdb.modules.security.config.bean.SecurityProperties;
import com.wdb.utils.RedisUtils;
import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.security.Key;
import java.util.ArrayList;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * @author: wendaobai@qq.com
 * @date: 2024/1/10 0010 16:05
 */
@Component
@Slf4j
@RequiredArgsConstructor
public class TokenProvider implements InitializingBean {
  private final SecurityProperties properties;
  public static final String AUTHORITIES_KEY = "user";
  private JwtParser jwtParser;
  private JwtBuilder jwtBuilder;
  private final RedisUtils redisUtils;

  @Override
  public void afterPropertiesSet() {
    byte[] keyBytes = Decoders.BASE64.decode(properties.getBase64Secret());
    Key key = Keys.hmacShaKeyFor(keyBytes);
    jwtParser = Jwts.parserBuilder().setSigningKey(key).build();
    jwtBuilder = Jwts.builder().signWith(key, SignatureAlgorithm.HS512);
  }

  public String createToken(Authentication authentication) {
    return jwtBuilder
        .setId(IdUtil.simpleUUID())
        .claim(AUTHORITIES_KEY, authentication.getName())
        .setSubject(authentication.getName())
        .compact();
  }

  public Claims getClaims(String token) {
    return jwtParser.parseClaimsJws(token).getBody();
  }
  /**
   * 获取登录用户RedisKey
   *
   * @param token /
   * @return key
   */
  public String loginKey(String token) {
    Claims claims = getClaims(token);
    String md5Token = DigestUtil.md5Hex(token);
    return properties.getOnlineKey() + claims.getSubject() + "-" + md5Token;
  }

  /**
   * 依据Token 获取鉴权信息
   *
   * @param token /
   * @return /
   */
  Authentication getAuthentication(String token) {
    Claims claims = getClaims(token);
    User principal = new User(claims.getSubject(), "******", new ArrayList<>());
    return new UsernamePasswordAuthenticationToken(principal, token, new ArrayList<>());
  }

  /** @param token 需要检查的token */
  public void checkRenewal(String token) {
    // 判断是否续期token,计算token的过期时间
    String loginKey = loginKey(token);
    long time = redisUtils.getExpire(loginKey) * 1000;
    Date expireDate = DateUtil.offset(new Date(), DateField.MILLISECOND, (int) time);
    // 判断当前时间与过期时间的时间差
    long differ = expireDate.getTime() - System.currentTimeMillis();
    // 如果在续期检查的范围内，则续期
    if (differ <= properties.getDetect()) {
      long renew = time + properties.getRenew();
      redisUtils.expire(loginKey, renew, TimeUnit.MILLISECONDS);
    }
  }

  public String getToken(HttpServletRequest request) {
    String header = properties.getHeader();
    log.info(header);
    String requestHeader = request.getHeader(header);
    log.info("requestHeader:{}", request.getHeaderNames());
    if (requestHeader != null && requestHeader.startsWith(properties.getTokenStartWith())) {
      return requestHeader.substring(7);
    }

    return null;
  }
}
